<?php

/**
 * @package Bike
 * @subpackage Db
 */

/**
 * Class for use mysql through PDO API
 * @author Chrome
 * @package Bike
 * @subpackage Db
 */
class Bike_Db_Pdo_Mysql extends PDO implements Bike_Db_Idb {

	/**
	 * Class constructor
	 * @param string $dbHost
	 * @param string $dbName
	 * @param string $dbUser
	 * @param string $dbPass
	 * @param array $dbOptions
	 * @param string $initialQuery
	 * @throws Bike_Db_Exception
	 */
	public function __construct($dbHost, $dbName, $dbUser, $dbPass, array $dbOptions = array(), $initialQuery = ''){
		if(!extension_loaded('pdo')){
			throw  new Bike_Db_Exception('The PDO extention is required for this database layer', Bike_Db_Exception::REQUIRED_EXTENSION_NOT_LOADED);
		}
		if(!in_array('mysql', PDO::getAvailableDrivers())){
			throw  new Bike_Db_Exception('The mysql driver for PDO extention is required for this database layer', Bike_Db_Exception::REQUIRED_EXTENSION_NOT_LOADED);
		}
		$dbOptions[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
		$dbOptions[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
		try{
			parent::__construct('mysql:dbname=' . $dbName . ';host=' . $dbHost, $dbUser, $dbPass, $dbOptions);
		}catch (PDOException $e){
				throw new Bike_Db_Exception($e->getMessage(), $e->getCode());
		}
		if(!empty($initialQuery)){
			$this->query($initialQuery);
		}
	}

	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::makeFilters()
	 */
	public function makeFilters(array $filters){
		$finalFilter = '';
		foreach ($filters as $filter){
			if('FIND_IN_SET' == strtoupper($filter['operation'])){
				$finalFilter .= (empty($finalFilter) ? ' ' : ' AND ') . ' FIND_IN_SET(' . $this->quote($filter['value']) . ', ' . $filter['field'] . ')';
			}else{
				$finalFilter .= (empty($finalFilter) ? ' ' : ' AND ') . ' `' . $filter['field'] . '` ' . $filter['operation'] . ' ' . $this->quote($filter['value']);
			}
		}
		return $finalFilter;
	}

	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::fetchData()
	 */
	public function fetchData($prefix, $name, $filters, $offset, $limit, array $sortings = array()){
		$limits = '';
		if(0 < $limit){
			$limits = ' LIMIT ' . $limit . ' OFFSET ' . $offset;
		}
		$sortings_array = array();
		if(!empty($sortings)){
			foreach($sortings as $field => $direction){
				$sortings_array[] = '`' . $field . '` ' . (strtolower($direction) == 'asc' ? 'ASC' : 'DESC');
			}
			$sortings_array[0] = ' ORDER BY ' . $sortings_array[0];
		}
		try{
			$result = array(
				'items' => $this->query('SELECT SQL_CALC_FOUND_ROWS * FROM `' . $prefix .  $name . '` ' . (!empty($filters) ? 'WHERE ' . $filters : '' ) . implode(', ', $sortings_array) . $limits),
				'totalCount' => $this->query('SELECT FOUND_ROWS()')->fetchColumn()
			);
		}catch(Bike_Db_Exception $e){
			$result = array('items' => array(), 'totalCount' => 0);
		}
		return $result;
	}

	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::update()
	 */
	public function update($prefix, $name, $id, array $data){
		$preparedData = array();
		foreach($data as $key => $value){
			$preparedData[] = '`' . $key . '` = ' . $this->quote($value) . ' ';
		}
		$queryStr = 'UPDATE `' . $prefix . $name . '` SET ' . implode(',', $preparedData) . ' WHERE `id` = ' . $this->quote($id);
		try{
			$this->query($queryStr);
		}catch(Bike_Db_Exception $e){
			return false;
		}
		return true;
	}

	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::insert()
	 */
	public function insert($prefix, $name, array $data){
		$fields = array();
		$values = array();
		foreach($data as $key => $value){
			$fields[] = '`' . $key . '`';
			$values[] = $this->quote($value);
		}
		$queryStr = 'INSERT INTO `' . $prefix . $name . '` (' . implode(', ', $fields) . ') VALUES (' . implode(',', $values) . ')';
		try{
			$this->query($queryStr);
		}catch(Bike_Db_Exception $e){
			return false;
		}
		return true;
	}
	
	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::query()
	 */
	public function query($query, $fetchMode = self::FETCH_RESULT_ASSOC){
		try {
			$stmt = parent::query($query);
			$resultMode = PDO::FETCH_ASSOC;
			switch($fetchMode){
				case self::FETCH_RESULT_NUM:
					$resultMode = PDO::FETCH_NUM;
					break;
				case self::FETCH_RESULT_BOTH:
					$resultMode = PDO::FETCH_BOTH;
					break;
			}
			$stmt->setFetchMode($resultMode);
			return $stmt;
		}catch(PDOException $e){
			throw new Bike_Db_Exception($e->getMessage(), Bike_Db_Exception::QUERY_ERROR);
		}
	}

	/* (non-PHPdoc)
	 * @see Bike_Db_Idb::delete()
	 */
	public function delete($prefix, $name, $id){
		$queryStr = 'DELETE FROM `' . $prefix . $name . '` WHERE `id` = ' . (int)$id;
		try{
			$this->query($queryStr);
		}catch(Bike_Db_Exception $e){
			return false;
		}
		return true;
	}
}
